/*
 * Created on 2006-6-26
 *
 * TODO To :普通通用数据库类型数据源检索对象。
 */
package com.powerunion.datacollection.report.excelreport.datasource;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

import com.powerunion.datacollection.report.excelreport.Logger;
import com.powerunion.datacollection.report.excelreport.config.DataSourceConfig;

/**
 * 数据库数据源类系统默认实现。 该数据源可以在配置文件中配置
 * SQL数据检索语句和参数， 数据源会生成SQL语句,并从自动从数据库检索数据到数据源中.
 * @author juny
 */
public class CommonDBDataSource extends DBDataSource {
    private static Logger log = Logger.getLogger(CommonDBDataSource.class);
    public CommonDBDataSource(){
        super();
    }
    
    public CommonDBDataSource(DataSourceConfig config, String name){
        super(config, name);
    }
    
    /**
     * 设置数据库连接对象
     * @param con 数据库连接对象实例
     * @throws Exception
     * @see DBConnection
     */
    public void setConnection(Connection con) throws Exception{
        if(null != con){
            this.connection = con;
            return;
        }
        throw new Exception("Error: Invalid DBConnection. (CommonDBDataSource.setDBConnection(DBConnection))");
    }
    
    /*
     *  (non-Javadoc)
     * @see excel.report.datasource.BaseDataSource#query(java.util.Map)
     */
    public int query(Map param) throws Exception {
        //作为数据库类型的数据源，如果没有连接则无法检索数据。
        if(null == this.connection){
            throw new Exception("Error: System couldn't find any database connection resource.");
        }
        
        List pms = getConfig().getParams();
        String sql = null;
        if(pms.size() != getParams().size()){
            throw new Exception("Absence paramaters! Datasource:" + getName());
        }else{
            sql = getSql();
            if(null != sql && !"".equals(sql)){
                //从数据库检索数据。
                query(sql);
                //移动到第一条记录。这点很重要。
                this.moveToFirst();
            }else{
                log.warn("Couldn't get sql from datasource " + this.getName());
            }    
        }
        
        return IDataSource.QUERY_SUCCESS;
    }
    
    /*
     *  (non-Javadoc)
     * @see excel.report.datasource.IDataSource#destroy()
     */
    public void destroy() throws SQLException{
        if(null != this.connection){
            this.connection.close();
        }
    }
    
    /*
     *  (non-Javadoc)
     * @see excel.report.datasource.IDataSource#getType()
     */
    public String getType(){
        return DataSourceConfig.DS_TYPE_DATABASE;
    }
    
    /**
     * 根据sql从数据库中检索记录，并将结果集自动填入到数据源中。
     * @param sql 要检索的sql语句。
     * @throws SQLException
     */
    private void query(String sql) throws SQLException{
        Statement stmt = null;
        log.debug(sql);
        try{
	        stmt = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
	        getData(stmt.executeQuery(sql));
        }finally{
            stmt.close();
        }
    }
    
    /**
     * 根据结果集得到所有字段名称
     * @param   ResultSet rs 结果集
     * @return  字段数组
     */
    protected static String[] getFieldsName(ResultSet rs) throws SQLException {
        ResultSetMetaData metaData = null;
        metaData = rs.getMetaData();
        int i = metaData.getColumnCount();
        String[] field = new String[i];
        for (int j = 0; j < i; j++) {
            field[j] = (metaData.getColumnName(j + 1)).toUpperCase();
        }
        return field;
    }
    
    /**
     * 获得结果集的总记录数
     * @param ResultSet rs 可滚动的结果集
     * @return int 如果可以滚动返回总记录数，否则抛出异常
     * @throw SQLException
     */
    protected static int getRow(ResultSet rs) throws SQLException {
        if (rs.getType() == ResultSet.TYPE_SCROLL_INSENSITIVE) {
            int row = rs.getRow();
            int nrow = 0;
            rs.last();
            nrow = rs.getRow();
            if (row == 0)
                rs.beforeFirst();
            else if (row == 1)
                rs.first();
            else
                rs.absolute(row);
            return nrow;
        } else {
            throw new SQLException("ResultSet is not removable!");
        }
    }

    /**
     * 这种保存数据的方法不是很可取，以后可能修改。每条记录都是一个Map，太浪费。
     * @param rs
     * @throws SQLException
     */
    protected void getData(ResultSet rs) throws SQLException{
        String[] fields = getFieldsName(rs);
        int totalRow = getRow(rs);

        int from = 0;

        if (totalRow != 0 && from < totalRow) {
            if (from == 0){
                rs.beforeFirst();
            }
            
            Object value = null;
            for (int i = 0; rs.next(); i++) {
                for (int j = 0; j < fields.length; j++) {
                    value = rs.getObject(fields[j]);
                    this.addField(fields[j], value);
                }
                this.addRecord();
            }
        }
    }
    
    protected Connection connection = null;
}
